package com.fm.demo.widget

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.os.Build
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.MotionEvent
import android.view.ViewGroup
import android.view.WindowManager
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.ImageView
import android.widget.RelativeLayout
import androidx.annotation.RequiresApi
import java.util.*
import kotlin.math.*


/**
 * 注意点：
 * 1.波纹点击效果（并且透明度随时间变化）
 * 2.鱼加速游动，先慢后块（用插值器）
 * 3.鱼的摆动速度（动画显示时长，其实就是扭尾巴的快慢），随机变化
 * 4.鱼游动的路径，贝塞尔曲线控制点的取值，起始点取imageView的左顶点
 * Created by Niwa on 2017/7/21.
 */
class FishView : RelativeLayout {
    /**
     * 屏幕宽
     */
    private var mScreenWidth = 0

    /**
     * 屏幕高
     */
    private var mScreenHeight = 0
    private var ivFish: ImageView = ImageView(context)
    private var fishDrawable: FishDrawable = FishDrawable()
    private var mPaint: Paint = Paint()
    private var alpha = 100
    private var canvas: Canvas? = null

    //当前按下的坐标点x和y
    private var _x = 0f
    private var _y = 0f

    /**
     * 波纹的半径
     */
    private var radius = 0f

    /**
     * 获取屏幕宽高
     */
    private val screenParams: Unit
        get() {
            val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
            val displayMetrics = DisplayMetrics()
            wm.defaultDisplay.getMetrics(displayMetrics)
            mScreenWidth = displayMetrics.widthPixels
            mScreenHeight = displayMetrics.heightPixels
        }

    /**
     * 波纹的线宽
     */
    private val _strokeWidth = 8f

    /**
     * 默认的波纹半径
     */
    private val _defaultRadius = 150f

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr, 0) {
        initStuff()
    }

    /**
     * 初始化各数据
     */
    private fun initStuff() {
        screenParams
        setWillNotDraw(false)
        //获取
        mPaint.let {
            it.isAntiAlias = true
            it.isDither = true
            it.style = Paint.Style.STROKE
            it.strokeWidth = _strokeWidth
        }
        val layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
        ivFish.layoutParams = layoutParams
        ivFish.setImageDrawable(fishDrawable)
        addView(ivFish)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        //设置当前的高宽
        setMeasuredDimension(mScreenWidth, mScreenHeight)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (this.canvas == null) {
            this.canvas = canvas
        }
        //点击后的波纹的颜色
        mPaint.setARGB(alpha, 0, 125, 251)
        //画波纹的圈，这里的半径随着动画引擎变化
        canvas.drawCircle(_x, _y, radius, mPaint)
    }


    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        _x = event.x
        _y = event.y
        val objectAnimator = ObjectAnimator.ofFloat(this, "radius", 0f, 1f)
        objectAnimator.start()

        //画鱼的游泳路径
        makeTrail(PointF(_x, _y))
        return super.onTouchEvent(event)
    }

    private fun makeTrail(touch: PointF) {
        val path = Path()
        val fishMiddlePointF = PointF(ivFish.x + fishDrawable.getMiddlePoint().x, ivFish.y + fishDrawable.getMiddlePoint().y)
        val fishHeadPointF = PointF(ivFish.x + fishDrawable.getHeadPoint().x, ivFish.y + fishDrawable.getHeadPoint().y)

        //将imageView的左顶点作为鱼的middlePoint， 就是点O
        path.moveTo(ivFish.x, ivFish.y)
        //要的转动的角度，示意图中的角AOB
        val angle = caculateScrollAngle(fishMiddlePointF, fishHeadPointF, touch)
        //控制点2  AOB夹角中线
        val controlPointF = calculatPoint(fishMiddlePointF, (1.6 * fishDrawable.getHeadRadius()).toFloat(), angle / 2)
        //A,C作为控制点，终点是触摸点偏左fishDrawable.getHeadPoint()距离（不明白这里的具体意思，感觉和示意图有点差别）
        path.cubicTo(
            fishHeadPointF.x,
            fishHeadPointF.y,
            controlPointF.x,
            controlPointF.y,
            touch.x - fishDrawable.getHeadPoint().x,
            touch.y - fishDrawable.getHeadPoint().y
        )
        val pos = FloatArray(2)
        val tan = FloatArray(2)
        val pathMeasure = PathMeasure(path, false)
        val animator = ObjectAnimator.ofFloat(ivFish, "x", "y", path)
        animator.duration = (2 * 1000).toLong()
        animator.interpolator = AccelerateDecelerateInterpolator()
        animator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationCancel(animation: Animator?) {
                super.onAnimationCancel(animation)
                //恢复扭动频率
                fishDrawable.setWaveFrequence(1f)
            }

            override fun onAnimationStart(animation: Animator?) {
                super.onAnimationStart(animation)
                //设定扭动频率
                fishDrawable.setWaveFrequence(2f)
                //动态设置扭动动画
                //获取FishDrawable里头的扭尾巴动画，然后动态设置，为了更骚
                val finsAnimator = fishDrawable.getFinsAnimator()
                //动画次数，体现出鱼的生物性
                finsAnimator.repeatCount = Random().nextInt(3)
                //动画显示时长，其实就是扭尾巴的快慢
                finsAnimator.duration = ((Random().nextInt(1) + 1) * 500).toLong()
                finsAnimator.start()
            }
        })
        animator.addUpdateListener { valueAnimator -> //属性改变百分比
            val persent = valueAnimator.animatedFraction
            //第一个参数一脸懵逼，个人理解应该是当前切线的轮廓乘以属性改变百分比
            pathMeasure.getPosTan(pathMeasure.length * persent, pos, tan)
            //根据切线值得到当前的切线角度，并按Android 的y轴在下面将y轴反转
            val actualAngle = (atan2((-tan[1]).toDouble(), tan[0].toDouble()) * 180.0 / Math.PI).toFloat()
            //动态设置角度到fishDrawable
            fishDrawable.setMainAngle(actualAngle)
        }
        animator.start()
    }

    /**
     * objectAnimators自动执行
     */
    fun setRadius(currentValue: Float) {
        alpha = (100 * (1 - currentValue) / 2).toInt()
        radius = _defaultRadius * currentValue
        postInvalidate()
    }

    /**
     * 要的转动的角度，示意图中的角AOB
     *
     * @param fishMiddlePoint O点
     * @param fishHeadPointF  A点
     * @param touch           触摸点
     * @return 要的转动的角度
     */
    private fun caculateScrollAngle(fishMiddlePoint: PointF, fishHeadPointF: PointF, touch: PointF): Float {
        //计算OA向量乘OB向量
        val abc =
            (fishHeadPointF.x - fishMiddlePoint.x) * (touch.x - fishMiddlePoint.x) + (fishHeadPointF.y - fishMiddlePoint.y) * (touch.y - fishMiddlePoint.y)
        //向量积除以向量模
        val angleCos = (abc /
                (sqrt(((fishHeadPointF.x - fishMiddlePoint.x) * (fishHeadPointF.x - fishMiddlePoint.x) + (fishHeadPointF.y - fishMiddlePoint.y) * (fishHeadPointF.y - fishMiddlePoint.y)).toDouble())
                        * sqrt(((touch.x - fishMiddlePoint.x) * (touch.x - fishMiddlePoint.x) + (touch.y - fishMiddlePoint.y) * (touch.y - fishMiddlePoint.y)).toDouble()))).toFloat()
        println(angleCos.toString() + "angleCos")

        //要的转动的角度，示意图中的角AOB
        val temAngle = Math.toDegrees(acos(angleCos.toDouble())).toFloat()
        //判断方向  正左侧  负右侧 0线上,但是Android的坐标系Y是朝下的，所以左右颠倒一下
        //向量共线定理，x1y2-x2y1
        val direction = (fishMiddlePoint.x - touch.x) * (fishHeadPointF.y - touch.y) - (fishMiddlePoint.y - touch.y) * (fishHeadPointF.x - touch.x)
        return if (direction == 0f) {
            //共线
            if (abc >= 0) {
                //abc大于0说明0到90度内，这里就是正方向的意思
                0f
            } else {
                180f
            }
        } else {
            if (direction > 0) { //右侧顺时针为负
                //x1y2>x2y1说明点在线的右边，考虑到Android的角度正方向是逆时针，示意图中是顺时针的角度这里直接取反减少干扰
                -temAngle
            } else {
                temAngle
            }
        }
    }

    /**
     * 输入起点、长度、旋转角度计算终点
     *
     *
     * 知道一个线段，一个定点，线段旋转角度求终点坐标
     * 根据极坐标系原理 x = pcog(a), y = psin(a)
     *
     * @param startPoint 起点
     * @param length     长度
     * @param angle      旋转角度
     * @return 计算结果点
     */
    private fun calculatPoint(startPoint: PointF, length: Float, angle: Float): PointF {
        val deltaX = cos(Math.toRadians(angle.toDouble())).toFloat() * length
        //符合Android坐标的y轴朝下的标准，和y轴有关的统一减180度
        val deltaY = sin(Math.toRadians((angle - 180).toDouble())).toFloat() * length
        return PointF(startPoint.x + deltaX, startPoint.y + deltaY)
    }

}